/**
 * ReportGear(2011)
 */
package com.reportgear.designer.gui.component.date;

import java.awt.Toolkit;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import javax.swing.text.PlainDocument;

import com.sot.core.util.StringUtils;

/**
 * 
 * 日期选择控件文档
 * 
 * @version 1.0 2011-4-26
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since Report 1.0
 */
public class JDateDocument extends PlainDocument {
	/**
	 * UID
	 */
	private static final long serialVersionUID = -7953773624507381168L;
	private JTextComponent textComponent;
	private SimpleDateFormat dateFormat;

	public JDateDocument(JTextComponent component, SimpleDateFormat format) throws UnsupportedOperationException {
		this(component, format, getCurrentDate(format));
	}

	public JDateDocument(JTextComponent component, SimpleDateFormat format, String text)
			throws UnsupportedOperationException {
		setDateFormat(format);
		this.textComponent = component;
		try {
			insertString(0, text, null);
		} catch (BadLocationException ex) {
			throw new UnsupportedOperationException(ex.getMessage());
		}
	}

	public void setDateFormat(SimpleDateFormat format) {
		this.dateFormat = format;
	}

	public SimpleDateFormat getDateFormat() {
		return this.dateFormat;
	}

	public static String getCurrentDate(SimpleDateFormat format) {
		return format.format(new Date());
	}

	public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
		if (str.length() == 1) {
			try {
				Integer.parseInt(str);
			} catch (Exception localException) {
				Toolkit.getDefaultToolkit().beep();
				return;
			}
			int i = offs;
			if ((offs == 4) || (offs == 7) || (offs == 10) || (offs == 13) || (offs == 16)) {
				this.textComponent.setCaretPosition(++i);
			}

			if (offs == this.dateFormat.toPattern().length()) {
				return;
			}

			String text = this.textComponent.getText();
			text = text.substring(0, i) + str + text.substring(i + 1);
			boolean bool = isValidDate(text);
			if (!bool) {
				Toolkit.getDefaultToolkit().beep();
				return;
			}
			super.remove(i, 1);
			super.insertString(i, str, a);
		} else {
			if ((str.length() != 10) && (str.length() != 19)) {
				return;
			}
			if (!(isValidDate(str))) {
				Toolkit.getDefaultToolkit().beep();
				return;
			}
			super.remove(0, getLength());
			super.insertString(0, str, a);
		}
	}

	public void remove(int offs, int len) throws BadLocationException {
		if ((offs == 4) || (offs == 7) || (offs == 10) || (offs == 13) || (offs == 16)) {
			this.textComponent.setCaretPosition(offs - 1);
		} else {
			this.textComponent.setCaretPosition(offs);
		}
	}

	private boolean isValidDate(String dataStr) {
		int validLen = getDateFormat().toPattern().length();

		if (dataStr == null) {
			return false;
		}

		dataStr = dataStr.trim();
		if (dataStr.length() != validLen) {
			return false;
		}

		for (int i = 0; i < 10; ++i) {
			if (dataStr.charAt(i) > 255) {
				return false;
			}
		}

		int year;
		int month;
		int date;
		try {
			year = Integer.parseInt(dataStr.substring(0, 4));
			month = Integer.parseInt(dataStr.substring(5, 7));
			date = Integer.parseInt(dataStr.substring(8, 10));
		} catch (Exception ex) {
			return false;
		}

		int caretPos = this.textComponent.getCaretPosition();
		boolean isValid = true;
		if ((month > 12) || (month < 1)) {
			month = Math.min(12, Math.max(1, month));
			isValid = false;
		}
		if (date < 1) {
			date = 1;
			isValid = false;
		}
		switch (month) {
		case 4:
		case 6:
		case 9:
		case 11:
			if (date > 30) {
				date = 30;
				isValid = false;
			}
			break;
		case 2:
			if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
				if (date > 29) {
					date = 29;
					isValid = false;
				}
			} else if (date > 28) {
				date = 28;
				isValid = false;
			}
			break;
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		default:
			if (date > 31) {
				date = 31;
				isValid = false;
			}
		}

		int hour = 0;
		int minute = 0;
		int seconds = 0;
		if (validLen > 10) {
			try {
				hour = Integer.parseInt(dataStr.substring(11, 13));
				minute = Integer.parseInt(dataStr.substring(14, 16));
				seconds = Integer.parseInt(dataStr.substring(17));
			} catch (Exception ex) {
				return false;
			}
			if ((hour > 23) || (hour < 0)) {
				hour = Math.min(23, Math.max(0, hour));
				isValid = false;
			}
			if ((minute > 59) || (minute < 0)) {
				minute = Math.min(59, Math.max(0, minute));
				isValid = false;
			}
			if ((seconds > 59) || (seconds < 0)) {
				seconds = Math.min(59, Math.max(0, seconds));
				isValid = false;
			}
		}
		if (!isValid) {
			this.textComponent.setText(toDateString(year, month, date, hour, minute, seconds));
			this.textComponent.setCaretPosition(caretPos + 1);
		}
		return isValid;
	}

	private String toDateString(int year, int month, int date, int hour, int minute, int second) {
		month = Math.max(1, Math.min(12, month));
		date = Math.max(1, Math.min(31, date));
		switch (month) {
		case 4:
		case 6:
		case 9:
		case 11:
			date = Math.min(30, date);
			break;
		case 2:
			if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
				date = Math.min(29, date);
			else
				date = Math.min(28, date);
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		}
		hour = Math.max(1, Math.min(24, hour));
		minute = Math.max(1, Math.min(59, minute));
		second = Math.max(1, Math.min(59, second));
		String formatStr = getDateFormat().toPattern();
		String strYear = StringUtils.padLeft("" + year, '0', 4);
		String strMonth = StringUtils.padLeft("" + month, '0', 2);
		String strDate = StringUtils.padLeft("" + date, '0', 2);
		String result = strYear + formatStr.substring(4, 5) + strMonth + formatStr.substring(7, 8) + strDate;
		if (formatStr.length() == 19)
			result = result + formatStr.substring(10, 11) + StringUtils.padLeft("" + hour, '0', 2)
					+ formatStr.substring(13, 14) + StringUtils.padLeft("" + minute, '0', 2)
					+ formatStr.substring(16, 17) + StringUtils.padLeft("" + second, '0', 2);

		return result;
	}
}